-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Refactoring URLSession for multiple native protocols #1216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@pushkarnk please review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand lots about this but had a quick look and left a few comments.
The code itself seems to be mostly moved from other files.
fatalError("Cant create DispatchIO channel") | ||
cleanupHandler: {_ in }) | ||
else { | ||
fatalError("Cant create DispatchIO channel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will fail if the file doesn't exist, why crash?
|
||
class _NativeProtocol: URLProtocol, _EasyHandleDelegate { | ||
internal var easyHandle: _EasyHandle! | ||
internal var totalDownloaded = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't see this being used anywhere, needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed ..
}() | ||
|
||
class _NativeProtocol: URLProtocol, _EasyHandleDelegate { | ||
internal var easyHandle: _EasyHandle! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this doesn't seem to get modified, let
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
easyHandle
is getting initialized during multiple init
and hence the compiler complains Immutable value 'self.easyHandle' may only be initialized once
internal var easyHandle: _EasyHandle! | ||
internal var totalDownloaded = 0 | ||
internal lazy var tempFileURL: URL = { | ||
let fileName = NSTemporaryDirectory() + NSUUID().uuidString + ".tmp" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- don't we have a better/safer way to create temp files somewhere
- is
NSTemporaryDirectory()
guaranteed to end in a/
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change has been incorporated into the code base through #1195 I have just refactored and pulled it to NativeProtocol as it can be used by multiple protocols like FTP and HTTP
@weissi -Thanks for the review. Yes its just refactoring of URLSession code .Earlier the code was tightly coupled with HTTP Protocol.To implement the support for other protocols like FTP I moved out the common code in to Native protocol .Please refer to the #1122 for more details on how this code will be used to support FTP and HTTP protocols. |
8b5ad15
to
17b0be5
Compare
17b0be5
to
09be77d
Compare
@swift-ci please test |
1 similar comment
@swift-ci please test |
I know this is an inherited problem and not caused by this PR, but the URLSession code is littered with |
Yeah, after perusing #1122 I have the same concern :-) I think it's a good time to bring it up to date with coding practices. |
return .proceed | ||
} | ||
|
||
func validateHeaderComplete(transferSate: _TransferState) -> URLResponse? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: transferSate -> transferState
} | ||
|
||
fileprivate func notifyDelegate(aboutReceivedData data: Data) { | ||
guard let t = self.task else { fatalError("Cannot notify") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This creates t
for the task but doesn't really use it. For example, the next line could be if case .taskDelegate(let delegate) = t.session.behaviour(for: t),
/// What action to take | ||
override func completionAction(forCompletedRequest request: URLRequest, response: URLResponse) -> _CompletionAction { | ||
// Redirect: | ||
let httpURLResponse = response as! HTTPURLResponse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern used elsewhere in the codebase is
guard let response = response as? HTTPURLResponse else {
fatalError("Reponse was not HTTPURLResponse")
}
//TODO: Should keep track of the number of redirects that this | ||
// request has gone through and err out once it's too large, i.e. | ||
// call into `failWith(errorCode: )` with NSURLErrorHTTPTooManyRedirects | ||
guard case .transferCompleted(response: let response, bodyDataDrain: let bodyDataDrain) = self.internalState else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
response
and bodyDataDrain
don't seem to be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its been used .
let url: URL | ||
/// Raw headers received. | ||
let parsedResponseHeader: _ParsedResponseHeader | ||
/// Once the headers is complete, this will contain the response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is
-> are
let requestBodySource: _BodySource? | ||
/// Body data received | ||
let bodyDataDrain: _NativeProtocol._DataDrain | ||
/// Describes what to do with received body data for this transfer: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this comment for?
/// The transfer completed. | ||
/// | ||
/// The easy handle has been removed from the multi handle. This does | ||
/// not (necessarily mean the task completed. A task that gets |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove (
I've done a first pass but feel like I only scratched the surface of this PR. There is so much that can be improved - not a criticism of @saiHemak who is working valiantly! |
09be77d
to
4060d34
Compare
70b606f
to
1cae8dd
Compare
@ianpartridge Yes, there's a lot of cleanup needed, with the fatalError() calls and in general. However, I am not sure if we should add that here. I'd prefer that done in a different (set of) pull request(s). What do you think? |
Yes, we should split up the work as much as possible. |
ad85d11
to
cf84a25
Compare
cf84a25
to
ddbad6d
Compare
Hi, would it be possible to avoid merging this refactoring before #1198? Merging in the opposite order will involve a good deal more “conflicts”. |
ddbad6d
to
4dca8bb
Compare
@ianpartridge @pushkarnk please review .. |
9661d13
to
ab72c5e
Compare
|
||
fileprivate func notifyDelegate(aboutReceivedData data: Data) { | ||
guard let t = self.task else { | ||
fatalError("Cannot notify") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The }
needs to be indented right.
|
||
func fill(writeBuffer buffer: UnsafeMutableBufferPointer<Int8>) -> _EasyHandle._WriteBufferResult { | ||
guard case .transferInProgress(let ts) = internalState else { | ||
fatalError("Requested to fill write buffer, but transfer isn't in progress.") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to have the }
on the next line
@xwu , Thanks for your time in reviewing this .I agree that this would take considerable amount of time to review as it cannot be directly compared with old diffs. However, as explained in the description this PR is all about refactoring the existing code to support multiple native protocols like FTP,HTTP ... Hence most of the code is same and it is just a movement from one file to other file. Refactoring is needed because FTP also reuses the most of the code written for HTTP like header parsing,code used for transfer.For more details, please refer to PR #1122 - Initial implementation to support FTP re-uses the same code. |
26610ea
to
5dbb4ba
Compare
@saiHemak For reviewers' benefit, can you please document explicitly, then, which code was moved from where, and what code is new? This could be done in text, but it would also be sufficient to have two commits here, one only moving existing code--with no additions whatsoever, maintaining all existing comments and formatting--then another commit with your changes to the code. |
Thanks for the inputs .. Actually, though the code is just a movement, few required the renaming too .. Here are the details of refactoring and reason behind refactoring ..
6. Moved easyHandle and tempFileURL instance variables to NativeProtocol
7. Moved implemented methods _HTTPURLProtocol: _EasyHandleDelegate . EasyHandleDelegate methods provides functionality for transfer irrespective of protocol to NativeProtocol- fill(writeBuffer buffer: UnsafeMutableBufferPointer) , However few methods like below are protocol specific hence have been overridden in respective protocol classes .
8.NativeProtocol has definition for configureEasyHandle(easyHandle) which needs concrete implementation which is protocol specific by sub-classes like FTP and HTTP.
Reason: Moved the code to Native Protocol as Transfer states have 1-to-1 with EasyHandle and irrespective of protocol As the complete TransferState code has been aligned to Native protocol TransferState.swift file has been removed . New Implementation : NativeProtocol.validateHeaderComplete isCompleteHeader -> Inner method Refer to point 11 reason.In order to support both protocols this method has been introduced |
It would be good to split the commits, with each commit message outlining the points you have made above, so that anyone reviewing the changes afterwards would have the context without having to go back to a GitHub PR to try and understand the why. It would also help reviewing the changes independently and validate each one separately, including that all of the moves are done without changing code (for sanity) and to then ensure that such moves continue to be built via the PR testing mechanism, instead of just the end result. |
019eb13
to
349d2cf
Compare
Moved lib curl specific code [libcurlHelpers.swift,EasyHandle.swift and MultiHandle.swift] from URLSession/http to URLSession/libcurl libcurl abstractions like EasyHandle and MultiHandle are not specific to HTTP.Hence moved out of the HTTP folder to a common licurl folder
…ectory Added libcurl abstractions to libcurl directory
The code respective to data handling is same irrespective to protocol.So moving it under URLSession folder.
Moved under URLSession as the code related to data handling like read backed up by a file or buffer is common to all protocols.
Transfer State has 1-0-1 relationship with Easy handle and is not bound to any protocol.So moved the code to NativeProtocol and renamed to _NativeProtocol. TransferState instead of HTTPTranserState
Created a class NativeProtocol which basically holds the common implementation of native protocols like HTTP , FTP and so on . It implements the URLProtocol and EashHandleDelegate.Moved enableLibcurlDebugOutput and enableDebugOutput debug flags from HTTPURLProtocol to NativeProtocol as these can be enabled on EasyHandle irrespective of the protocol.Moved _HTTPURLProtocol.InternalState from HTTPURLProtocol to _NativeProtocol.InternalState. Moved inits from HTTPURLProtocol to NativeProtocol.Moved easyHandle and tempFileURL instance variables to NativeProtocol.EasyHandleDelegate methods provides functionality for transfer irrespective of protocol to NativeProtocol- fill(writeBuffer buffer: UnsafeMutableBufferPointer) , notifyDelegate(aboutUploadedData), notifyDelegate(aboutReceivedData), didReceive(data). transferCompleted(withError error: NSError?) createTransferState() createTransferBodyDataDrain However few methods like below are protocol specific hence have been overridden in respective protocol classes . didReceive(headerData data: Data, contentLength: Int64) Though the headers basically ends with \r\n irrespective of protocol the header format in FTP is different from HTTP. For e.g.: Headers in FTP looks like a plain text header with status code and description unlike the key-value pair headers in HTTP. NativeProtocol has definition for configureEasyHandle(easyHandle) which needs concrete implementation which is protocol specific by sub-classes like FTP and HTTP. For e.g.:HTTP requires some more additional configurations like adding the headers, setting allowed protocols. Moved HTTPURLProtocol. _ParsedResponseHeader from HTTPMessage.swift to NativeProtocol.swift under _NativeProtocol._ParsedResponseHeader . This implementation basically does header parsing based on \r\n delimiters. Except the fact that empty header is received in case of HTTP to mark as Header completion and 150 status marks the open of data communication in FTP New Implementation : NativeProtocol.validateHeaderComplete Should not throw error incase of simple-responseshttps://github.com/swiftlang/pull/1097 isCompleteHeader -> Inner method In order to support both protocols this method has been introduced -> which returns to true when the header is empty in case of HTTP. -> Returns true if the header starts with 150
Moved HTTPURLProtocol. _ParsedResponseHeader from HTTPMessage.swift to NativeProtocol.swift under _NativeProtocol._ParsedResponseHeader. This implementation basically does header parsing based on \r\n delimiters. Except the fact that empty header is received in case of HTTP to mark as Header completion and 150 status marks the open of data communication in FTP.
Moved methods specific to EasyHandle,common variables and common initializers to NativeProtocol.swift. Also overridden methods like configureEasyHandle which are specific to HTTP
349d2cf
to
976f82b
Compare
@alblue @pushkarnk @xwu I have split the commit ,please review |
The delete/add commits (64429de and a44a9f9) should really be one commit, not two. That way git will be able to figure out that there has been a move between files, rather than a mass-delete and mass-add. In fact, it might be better to spin that combined commit (of 64429de and a44a9f9) into its own separate review first of all, rebased against master. Once that can be verified it's not breaking any tests on its own, it can be merged - and then the body of this PR can be rebased upon it and we can review the changes subsequently. So, I recommend:
Sound good? |
@alblue : Sure |
Likewise subsequent pairs of commits that move code. The point is to have git demonstrate that each such a change is only moving code, never adding or deleting anything. It is not possible to see this on inspection when one commit removes the code and another adds it back. |
Since we're breaking this apart and filing separate changes, I'm going to close this request as is. Will be happy to look at the incremental changes (such as the one merged previously) in order to get the functionality in. Thanks for keeping up with this! |
The code in this PR is a part of #1122 and has been separated to reduce the size of the code change.
#968 refactored URLSession to decouple it from the HTTP implementation and allow native protocols to be implemented as URLProtocols, uniform with custom protocols. With this PR, we go a step further, and factor out common code which can be used with multiple native protocols. All of this code is moved to a new class called
_NativeProtocol
.The existing HTTP implementation and the FTP Implementation [#1122] will be reusing
_NativeProtocol
.